<script lang="ts" setup>
import type { FormInstance } from 'element-plus';

import type { DataFormOptionsProps, DataFormProps } from './types';

import { nextTick, onMounted, reactive, ref } from 'vue';

import { VIcon } from '@croco/framework/icons';

import { service } from '#/api/request';

const props = withDefaults(defineProps<DataFormProps>(), {
    api: '',
    fields: () => [],
    method: 'post',
    width: '45vh',
    footer: true,
    fullscreen: false,
    beforeSubmit: () => true,
});

const dataFormRef = ref<FormInstance>();
const visible = ref(false);
const loading = ref(false);
const isFullscreen = ref(props.fullscreen);
const mode = ref(props.method);
const primaryKey = reactive<string[]>([]);
const options = ref<DataFormOptionsProps>({});

// 定义表单数据对象
const form = ref<Record<string, any>>({});

// 提交表单
const handleSubmit = () => {
    dataFormRef.value?.validate(async (valid: boolean) => {
        if (valid) {
            const result = await props.beforeSubmit(form);
            if (result) {
                loading.value = true;
                service[mode.value](props.api, form.value, { headers: options.value?.headers })
                    .then(options.value.success)
                    .then(handleClose)
                    .catch(options.value.failure)
                    .finally(() => (loading.value = false));
            }
        }
    });
};

// 关闭弹窗
const handleClose = () => {
    dataFormRef.value?.resetFields();
    visible.value = false;
};

const handleToggleFullscreen = () => {
    isFullscreen.value = !isFullscreen.value;
};

// 打开弹窗
const open = (opt: DataFormOptionsProps): void => {
    options.value = opt;
    // 根据主键字段是否有值判断本次动作类型
    if (props.method === 'get') {
        mode.value = 'get';
    } else {
        mode.value = primaryKey.every((key: string) => !!options.value.data?.[key]) ? 'put' : 'post';
    }
    isFullscreen.value = props.fullscreen;
    // 显示弹窗并加载数据
    visible.value = true;
    if (options.value.data) {
        nextTick(() => props.fields.forEach((item) => (form.value[item.prop] = options.value.data?.[item.prop])));
    }
};

// 定义字段列表
onMounted(() => {
    for (const field of props.fields) {
        field.primary && primaryKey.push(field.prop);
        form.value[field.prop] = field.value ?? undefined;
    }
});

// 暴露接口
defineExpose({ open });
</script>

<template>
    <el-dialog v-model="visible" :fullscreen="isFullscreen" :close-on-click-modal="false" :show-close="false" :width="width" align-center destroy-on-close draggable overflow>
        <template #header="{ titleId, titleClass }">
            <div class="dialog-header">
                <div class="dialog-header--title">
                    <el-icon :size="18" style="margin-right: 5px">
                        <VIcon :icon="mode === 'put' ? 'icon:edit-pen' : mode === 'post' ? 'icon:edit' : 'icon:chat-line-square'" />
                    </el-icon>
                    <span :id="titleId" :class="titleClass">{{ (mode === 'put' ? '编辑' : mode === 'post' ? '创建' : '查看') + props.title }}</span>
                </div>
                <div>
                    <el-button class="dialog-header--fullscreen" @click="handleToggleFullscreen()" link>
                        <template #icon>
                            <VIcon :icon="isFullscreen ? 'ph:copy-simple-thin' : 'ph:stop-thin'" />
                        </template>
                    </el-button>
                    <el-button class="dialog-header--close" @click="handleClose()" link>
                        <template #icon>
                            <VIcon icon="icon:close" />
                        </template>
                    </el-button>
                </div>
            </div>
        </template>
        <el-form ref="dataFormRef" :model="form" label-width="auto" label-position="right" :rules="rules">
            <template v-for="item in fields" :key="item.prop">
                <el-form-item v-if="item.primary" :label="item.label" :prop="item.prop" v-show="item.label">
                    <el-input v-model="form[item.prop]" disabled v-bind="item.attrs" />
                </el-form-item>
                <slot v-else-if="typeof item.slot === 'string'" :name="item.slot" :label="item.label" :form="form" :attrs="item.attrs"></slot>
                <el-form-item v-else-if="item.slot !== false" :label="item.label" :prop="item.prop">
                    <template v-if="item.tooltip" #label="{ label }">
                        <div class="flex-center">
                            <span>{{ label }}</span>
                            <el-tooltip :content="item.tooltip" placement="right">
                                <VIcon icon="icon:question-filled" />
                            </el-tooltip>
                        </div>
                    </template>
                    <slot :name="item.prop" :form="form" :attrs="item.attrs">
                        <el-date-picker
                            v-if="item.format"
                            v-model="form[item.prop]"
                            :type="item.format.includes('HH') ? 'datetime' : 'date'"
                            :format="item.format"
                            :value-format="item.format"
                            :placeholder="`请选择${item.label}`"
                            clearable
                            style="width: 100%"
                            v-bind="item.attrs"
                        />
                        <el-select v-else-if="item.multiple !== undefined" v-model="form[item.prop]" :placeholder="`请选择${item.label}`" :multiple-limit="3" clearable v-bind="item.attrs">
                            <el-option v-for="(option, index) in enums?.[item.prop]" :key="index" :label="option.label" :value="option.value" />
                        </el-select>
                        <el-input v-else v-model="form[item.prop]" :placeholder="`请输入${item.label}`" clearable v-bind="item.attrs" />
                    </slot>
                </el-form-item>
            </template>
        </el-form>
        <template v-if="footer" #footer>
            <el-button type="danger" @click="handleClose()">取消</el-button>
            <el-button v-if="mode !== 'get'" :loading="loading" type="primary" @click="handleSubmit()">确定</el-button>
        </template>
    </el-dialog>
</template>

<style lang="scss" scoped>
.dialog-header {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    gap: 16px;

    &--title {
        display: flex;
        align-items: center;
    }

    &--close,
    &--fullscreen {
        font-size: 18px;
    }

    &--close:hover {
        color: var(--el-color-primary);
    }
}
</style>
